﻿using System;
using System.IO;
using System.Collections;

namespace RVBike
{
    public class RVMovie
    {
        public int ID { get; set; }
        public string VideoPath { get; set; }
        public int Mileage { get; set; }
        public string MetafilePath { get; set; }
    }

    class RVSamplePoint
    {
        public class Comp : IComparer
        {
            int IComparer.Compare(Object lhs, Object rhs)
            {
                RVSamplePoint l = (RVSamplePoint)lhs;
                RVSamplePoint r = (RVSamplePoint)rhs;
                return l.Mileage.CompareTo(r.Mileage);
            }
        }

        public float Mileage { get; set; }
        public float Slope { get; set; }
        public float Reserve1 { get; set; }
        public float GPSX { get; set; }
        public float GPSY { get; set; }
    }

    public class RVMovieContext
    {
        private ArrayList _samples = null;
        private int _current_index = 0;

        public RVMovieContext(RVMovie movie)
        {
            if (movie == null)
            {
                throw new System.ArgumentNullException("RVMovie");
            }

            if (!LoadMovie(movie))
            {
                throw new System.Exception(String.Format("Faild to load context of movie[{0}]", movie.ID));
            }
        }

        public void Uninit()
        {
            if (_samples != null)
            {
                _samples.Clear();
                _samples = null;
            }
        }

        private bool LoadMovie(RVMovie movie)
        {
            _samples = new ArrayList();

            // Read the file and display it line by line.
            using (StreamReader file = new StreamReader(movie.MetafilePath))
            {
                string line;
                char[] delimiter = {'\t'};
                while ((line = file.ReadLine()) != null)
                {
                    string[] words = line.Split(delimiter);
                    RVSamplePoint sample = new RVSamplePoint();
                    sample.Mileage = float.Parse(words[1]);
                    sample.Slope = float.Parse(words[2]);
                    sample.Reserve1 = float.Parse(words[3]);
                    sample.GPSX = float.Parse(words[5]);
                    sample.GPSY = float.Parse(words[4]);
                    _samples.Add(sample);
                }
            }

            _current_index = 0;
            return true;
        }

        public bool GoToMileage(float mileage, bool search)
        {
            RVSamplePoint target = new RVSamplePoint();
            target.Mileage = mileage;

            int index = 0;
            if (search)
            {
                index = _samples.BinarySearch(target, new RVSamplePoint.Comp());
                if (index < 0)
                {
                    index = ~index;
                }

                if (index == _samples.Count)
                {
                    return false;
                }
            }
            else
            {
                index = _current_index;
                for (; index < _samples.Count; ++index)
                {
                    if (target.Mileage < ((RVSamplePoint)_samples[index]).Mileage)
                    {
                        --index;
                        break;
                    }
                }
            }

            if (index < 0 || index == _samples.Count)
            {
                return false;
            }

            _current_index = index;
            return true;
        }

        public int CurrentFrameIndex
        {
            get
            {
                return _current_index;
            }
        }

        public float Time
        {
            get
            {
                // Assume Movie is encoded with 30 FPS
                const float INTERVAL_30_FPS = 1.0f / 30.0f;
                return _current_index * INTERVAL_30_FPS;
            }
        }

        public float Mileage 
        {
            get
            {
                return ((RVSamplePoint)_samples[_current_index]).Mileage;
            }
        }

        public float Slope
        {
            get
            {
                return ((RVSamplePoint)_samples[_current_index]).Slope;
            }
        }

        public float Reserve1
        {
            get
            {
                return ((RVSamplePoint)_samples[_current_index]).Reserve1;
            }
        }

        public float GPSX
        {
            get
            {
                return ((RVSamplePoint)_samples[_current_index]).GPSX;
            }
        }

        public float GPSY
        {
            get
            {
                return ((RVSamplePoint)_samples[_current_index]).GPSY;
            }
        }
    }
}
